/*
	Copyright (C) 2010 Salil Bhagurkar

	This file is part of k3

	k3 is free software: you can redistribute it and/or modify
	it under the terms of the GNU Lesser General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	k3 is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU Lesser General Public License for more details.

	You should have received a copy of the GNU Lesser General Public License
	along with k3.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <shared.h>

struct tagvalue {
	char *tag;
	char *value;
	u32 valuelen;
	struct tagvalue *next, *prev;
};

/**
 * Tree formation zone
 * Form a tree from the sequential list of tags and values
 */
static struct fs *xmlp_create_fs(struct tagvalue *tv)
{
	dprint(D_INFO, tv->value, tv->tag);
	struct fs *n = fs_create(FSFLAGS_DATA, tv->tag);
	n->buf.len = n->buf.alloc_len = tv->valuelen;
	n->buf.data = malloc_get(n->buf.len, "xmlp");
	memcpy(n->buf.data, tv->value, tv->valuelen);
	return n;
}

static struct tagvalue *xmlp_create_tree(struct fs *in, struct tagvalue *list_start)
{
	struct tagvalue *t;
	for(t = list_start; t;) {
		if(t->tag[0] != '/' && t->next) {
			//Find a node that has ended on the next one or a special tag
			if(t->next->tag[0] == '/' || t->next->tag[0] == '&') {
				dprint(D_INFO, t->tag, "Found");
				struct fs *n = xmlp_create_fs(t);
				fs_link(in, n);
				t = t->next->next;
			} else {
				dprint(D_INFO, t->tag, "Found");
				dprint(D_INFO, "", "Recurse");
				struct fs *n = xmlp_create_fs(t);
				fs_link(in, n);
				t = xmlp_create_tree(n, t->next);
				t = t->next;
			}
		} else if(t->tag[0] == '/') {
			return t;
		}
	}
}

/**
 * tagvalue zone
 * This part generates a sequential list of all tags with the corresponding
 * value within them
 */
static struct tagvalue *xmlp_create_tagvalue(char *tag, char *value, u32 taglen, u32 valuelen)
{
	struct tagvalue *n;
	n = (struct tagvalue *)malloc_get(sizeof(struct tagvalue), "xmlp");
	char *newtag, *newvalue;
	newtag = (char *)malloc_get(taglen, "xmlp");
	newvalue = (char *)malloc_get(valuelen, "xmlp");
	memcpy(newtag, tag, taglen);
	memcpy(newvalue, value, valuelen);
	n->tag = newtag;
	n->value = newvalue;
	n->valuelen = valuelen;
	n->next = n->prev = NULL;
	return n;
}

static void xmlp_free_tagvalue(struct tagvalue *tv)
{
	malloc_free(tv->tag);
	malloc_free(tv->value);
}

static struct tagvalue *xmlp_xml_to_list(char *input)
{
	struct tagvalue *tagvalues = NULL, *tagvaluestail = NULL;
	u32 index, len;
	bool flag_in_tag = false;
	u32 ti = 0, vi = 0, ti_end = 0, vi_end = 0;
	len = strlen(input);
	for(index = 0; index < len; index++) {
		if(input[index] == '<') {
			flag_in_tag = true;
			if(ti != ti_end) {
				struct tagvalue *n;
				if(vi != vi_end) {
					n = xmlp_create_tagvalue(input + ti, input + vi, ti_end - ti + 1, vi_end - vi + 1);
				} else {
					n = xmlp_create_tagvalue(input + ti, input + vi, ti_end - ti + 1, 0);
				}
				LIST_ATTACH(tagvalues, tagvaluestail, n);
			}
			ti = ti_end = index + 1;
			vi = vi_end = index + 1;
		} else if(input[index] == '>') {
			flag_in_tag = false;
			vi = vi_end = index + 1;
		} else {
			if(flag_in_tag) {
				ti_end = index;
			} else {
				vi_end = index;
			}
		}
	}
	if(ti != ti_end) {
		struct tagvalue *n;
		if(vi != vi_end) {
			n = xmlp_create_tagvalue(input + ti, input + vi, ti_end - ti + 1, vi_end - vi + 1);
		} else {
			n = xmlp_create_tagvalue(input + ti, input + vi, ti_end - ti + 1, 0);
		}
		LIST_ATTACH(tagvalues, tagvaluestail, n);
	}
	return tagvalues;
}

/**
 * Write function of the driver
 * Converts to an fs tree at the referrer
 */
static int xmlp_write(struct fs *referrer, struct fs *this, struct buffer *buf, u32 offset, u32 count)
{
	struct tagvalue *list;
	list = xmlp_xml_to_list(buf->data);
	xmlp_create_tree(referrer, list);
	return 0;
}


/**
 * Unparser zone
 * Converts an fs tree into xml
 */

#define BASE_XML_ALLOC 100

/**
 * Write a string to the xml output buffer
 * The buffer grows in size as we write to it
 */
static void xmlp_write_xml_output(char **buffer, u32 *len, u32 *alloc_len, char *thing, u32 thing_len)
{
	//Reallocate if more memory is required
	if(((*len) + thing_len) > (*alloc_len)) {
		(*alloc_len) *= 2;
		*buffer = malloc_resize(*buffer, (*alloc_len));
	}
	//Copy data to buffer
	memcpy((*buffer) + *len, thing, thing_len);
	(*len) += thing_len;
}

static void xmlp_fs_to_xml_base(char **xml, u32 *len, u32 *alloc_len, struct fs *fs)
{
	int err;
	xmlp_write_xml_output(xml, len, alloc_len, "<", 1);
	xmlp_write_xml_output(xml, len, alloc_len, fs->name, strlen(fs->name));
	xmlp_write_xml_output(xml, len, alloc_len, ">", 1);
	xmlp_write_xml_output(xml, len, alloc_len, fs->buf.data, fs->buf.len);
	struct fs *ch = fs;
	err = fsiter_get_child(&ch);
	while(!err) {
		xmlp_fs_to_xml_base(xml, len, alloc_len, ch);
		err = fsiter_get_next(&ch);
	}
	xmlp_write_xml_output(xml, len, alloc_len, "</", 2);
	xmlp_write_xml_output(xml, len, alloc_len, fs->name, strlen(fs->name));
	xmlp_write_xml_output(xml, len, alloc_len, ">", 1);
}

/**
 * Traverse through the fs tree and return xml data
 */
static char *xmlp_fs_to_xml(struct fs *from, u32 *out_len)
{
	char *xml;
	u32 alloc_len = BASE_XML_ALLOC, len = 0;
	xml = (char *)malloc_get(alloc_len, "xmlp");
	xmlp_fs_to_xml_base(&xml, &len, &alloc_len, from);
	*out_len = len;
	return xml;
}


/**
 * Device read function
 * Returns xml from the referrer fs structure
 */
static int xmlp_read(struct fs *referrer, struct fs *this, struct buffer *buf, u32 offset, u32 count)
{
	char *xml;
	u32 len;
	xml = xmlp_fs_to_xml(referrer, &len);
	buffer_write_data(buf, xml, 0, len);
	malloc_free(xml);
}

void register_xmlparse()
{
	struct fs *xmlp_fs = fs_create(FSFLAGS_DEVICE, "xmlp");
	u32 len;
	xmlp_fs->system = fs_create_system(0, xmlp_read, xmlp_write, fs_system_generic_rename, xmlp_fs);
	fs_link(fsparse_get("/devices/lib"), xmlp_fs);
	
	struct buffer *buf = buffer_from_string("<firstNode><&mode>Read</&mode><&base>/devices/firstNode</&base><&data>Holy Shit!</&data></firstNode>");
	xmlp_write(fsparse_get("/"), xmlp_fs, buf, 0, buf->len);
	buffer_free(buf);
	
/*	while(1) {
		arch_halt();
	}*/
}
